/**
 * EDF - Tous droits réservés &copy; 2012 - 2014
 */
package com.mkl.leek.dpsSimulator.main;

import org.apache.commons.lang.math.Range;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

/**
 * There should be some comments here.
 * <p>
 * Dernier commit:
 * <ul>
 * <li>le $Date:$</li>
 * <li>par $Author:$</li>
 * <li>revision $Revision:$</li>
 * </ul>
 * </p>
 * 
 * @author MKL
 */
public class OldMainThatStinks {

    public static void main(String[] args) throws Exception {
        testLeek();
        //testLeekOld();
    }

    public static void testLeek() {
        BigDecimal agi = new BigDecimal("0");
        BigDecimal force = new BigDecimal("0");
        BigDecimalRange degatsDoubleGun = new BigDecimalRange(new BigDecimal("18"), new BigDecimal("25"));
        BigDecimalRange degatsDoubleGunLeger = new BigDecimalRange(new BigDecimal("5"), new BigDecimal("8"));
        BigDecimalRange degatsShootGun = new BigDecimalRange(new BigDecimal("33"), new BigDecimal("43"));
        BigDecimalRange degatsLaser = new BigDecimalRange(new BigDecimal("43"), new BigDecimal("59"));
        BigDecimalRange degatsGrenadeLauncher = new BigDecimalRange(new BigDecimal("45"), new BigDecimal("53"));
        BigDecimalRange degatsShock = new BigDecimalRange(new BigDecimal("5"), new BigDecimal("7"));
        BigDecimalRange degatsSpark = new BigDecimalRange(new BigDecimal("8"), new BigDecimal("16"));
        BigDecimalRange degatsFlamme = new BigDecimalRange(new BigDecimal("25"), new BigDecimal("27"));
        BigDecimalRange soinsBandage = new BigDecimalRange(new BigDecimal("10"), new BigDecimal("15"));
        BigDecimalRange soinsCure = new BigDecimalRange(new BigDecimal("35"), new BigDecimal("43"));
        AbsoluteEffect helmet = new AbsoluteEffect(new BigDecimal("15"), 3);
        AbsoluteEffect stretching = new AbsoluteEffect(new BigDecimal("50"), 2);
        AbsoluteEffect proteines = new AbsoluteEffect(new BigDecimal("50"), 2);
        AbsoluteEffect steroides = new AbsoluteEffect(new BigDecimal("100"), 3);

        Leek joueur1 = new Leek(agi, force);
        Leek joueur2 = new Leek(agi, force);
        Leek joueur3 = new Leek(agi, force);
        Leek joueur4 = new Leek(agi, force);

        // Joueur 1 vs Joueur 2
        // tour 1 casque + guerison
        joueur1.addShield(helmet, 1);
        joueur1.addHeal(soinsCure, 1);
        // tour 2 shotgun + bandage
        joueur1.addAttack(degatsShootGun, 2);
        joueur1.addHeal(soinsBandage, 2);
        // tour 3 boost force + shotgun
        joueur1.addForBuff(proteines, 3);
        joueur1.addAttack(degatsShootGun, 3);
        // tour 4 shootgun + etincelle
        joueur1.addAttack(degatsShootGun, 4);
        joueur1.addAttack(degatsSpark, 4);

        // tour 1 etirement (deja compte) + casque
        joueur2.addAgiBuff(stretching, 1);
        joueur2.addShield(helmet, 1);
        // tour 2 guerison, bandage et decharge
        joueur2.addHeal(soinsCure, 2);
        joueur2.addHeal(soinsBandage, 2);
        joueur2.addAttack(degatsShock, 2);
        // tour 3 boost force + double gun
        joueur2.addForBuff(proteines, 3);
        joueur2.addAttack(degatsDoubleGun, 3);
        joueur2.addAttack(degatsDoubleGunLeger, 3);
        // tour 4 double gun * 2
        joueur2.addAttack(degatsDoubleGun, 4);
        joueur2.addAttack(degatsDoubleGunLeger, 4);
        joueur2.addAttack(degatsDoubleGun, 4);
        joueur2.addAttack(degatsDoubleGunLeger, 4);

//        System.out.println("DEBUG: " + joueur1.getTotalHeals());
//        System.out.println("DEBUG: " + joueur2.getTotalHeals());
//        System.out.println("DEBUG: " + joueur1.getTotalDamage(joueur2));
//        System.out.println("DEBUG: " + joueur2.getTotalDamage(joueur1));

        System.out.println("Joueur 1: " + joueur1.versus(joueur2));
        System.out.println("Joueur 2: " + joueur2.versus(joueur1));


        // tour 1 casque + flamme
        joueur3.addShield(helmet, 1);
        joueur3.addAttack(degatsFlamme, 1);
        // tour 2 guerison + flamme
        joueur3.addHeal(soinsCure, 2);
        joueur3.addAttack(degatsFlamme, 2);
        // tour 3 boost force + flamme
        joueur3.addForBuff(proteines, 3);
        joueur3.addAttack(degatsFlamme, 3);
        // tour 4 double gun * 2
        joueur3.addAttack(degatsDoubleGun, 4);
        joueur3.addAttack(degatsDoubleGunLeger, 4);
        joueur3.addAttack(degatsDoubleGun, 4);
        joueur3.addAttack(degatsDoubleGunLeger, 4);

        System.out.println("Joueur 1: " + joueur1.versus(joueur3));
        System.out.println("Joueur 3: " + joueur3.versus(joueur1));

        // tour 1 casque + guerison
        joueur4.addShield(helmet, 1);
        joueur4.addHeal(soinsCure, 1);
        // tour 2 steroides + bandage
        joueur4.addForBuff(steroides, 2);
        joueur4.addHeal(soinsBandage, 2);
        // tour 3 shootgun + etincelle
        joueur4.addAttack(degatsShootGun, 3);
        joueur4.addAttack(degatsSpark, 3);
        // tour 4 shootgun + etincelle
        joueur4.addAttack(degatsShootGun, 4);
        joueur4.addAttack(degatsSpark, 4);

        System.out.println("Joueur 1: " + joueur1.versus(joueur4));
        System.out.println("Joueur 4: " + joueur4.versus(joueur1));
    }

    public static void testLeekOld() {
        BigDecimal agiJoueur1 = new BigDecimal("0");
        BigDecimal agiJoueur2 = new BigDecimal("50"); // on compte etirement
        BigDecimal agiJoueur3 = new BigDecimal("0");
        BigDecimal forJoueur1 = new BigDecimal("100");
        BigDecimal forJoueur2 = new BigDecimal("100");
        BigDecimal forJoueur3 = new BigDecimal("100");
        BigDecimalRange soinsJoueur1 = new BigDecimalRange();
        BigDecimalRange soinsJoueur2 = new BigDecimalRange();
        BigDecimalRange soinsJoueur3 = new BigDecimalRange();
        BigDecimalRange degatsJoueur1 = new BigDecimalRange();
        BigDecimalRange degatsJoueur2 = new BigDecimalRange();
        BigDecimalRange degatsJoueur3 = new BigDecimalRange();
        BigDecimalRange degatsDoubleGun = new BigDecimalRange(new BigDecimal("18"), new BigDecimal("25"));
        BigDecimalRange degatsDoubleGunLeger = new BigDecimalRange(new BigDecimal("5"), new BigDecimal("8"));
        BigDecimalRange degatsShootGun = new BigDecimalRange(new BigDecimal("33"), new BigDecimal("43"));
        BigDecimalRange degatsShock = new BigDecimalRange(new BigDecimal("5"), new BigDecimal("7"));
        BigDecimalRange degatsSpark = new BigDecimalRange(new BigDecimal("8"), new BigDecimal("16"));
        BigDecimalRange degatsFlamme = new BigDecimalRange(new BigDecimal("25"), new BigDecimal("27"));
        BigDecimalRange soinsBandage = new BigDecimalRange(new BigDecimal("10"), new BigDecimal("15"));
        BigDecimalRange soinsCure = new BigDecimalRange(new BigDecimal("35"), new BigDecimal("43"));
        AbsoluteEffect helmet = new AbsoluteEffect(new BigDecimal("15"), 3);
        AbsoluteEffect stretching = new AbsoluteEffect(new BigDecimal("50"), 2);
        AbsoluteEffect proteines = new AbsoluteEffect(new BigDecimal("50"), 2);
        BigDecimal helmetOld = new BigDecimal("15");
        BigDecimal boost = new BigDecimal("50");
        BigDecimal agiMod1 = getMod(agiJoueur1);
        BigDecimal agiMod2 = getMod(agiJoueur2);
        BigDecimal agiMod3 = getMod(agiJoueur3);
        BigDecimal forMod1 = getMod(forJoueur1);
        BigDecimal forMod2 = getMod(forJoueur2);
        BigDecimal forMod3 = getMod(forJoueur3);

        // Joueur 1 vs Joueur 2
        // tour 1 casque + guerison
        soinsJoueur1 = soinsJoueur1.add(soinsCure.multiply(agiMod1));
        // tour 2 shotgun + bandage
        degatsJoueur1 = degatsJoueur1.add(degatsShootGun.multiply(forMod1).substract(helmetOld.multiply(agiMod2)));
        soinsJoueur1 = soinsJoueur1.add(soinsBandage.multiply(agiMod1));
        // tour 3 boost force + shotgun
        degatsJoueur1 = degatsJoueur1.add(degatsShootGun.multiply(getMod(forJoueur1.add(boost))).substract(helmetOld.multiply(agiMod2)));
        // tour 4 shootgun + etincelle
        degatsJoueur1 = degatsJoueur1.add(degatsShootGun.multiply(getMod(forJoueur1.add(boost))));
        degatsJoueur1 = degatsJoueur1.add(degatsSpark.multiply(getMod(forJoueur1.add(boost))));

        // tour 1 etirement (deja compte) + casque
        // tour 2 guerison, bandage et decharge
        soinsJoueur2 = soinsJoueur2.add(soinsCure.multiply(agiMod2));
        soinsJoueur2 = soinsJoueur2.add(soinsBandage.multiply(agiMod2));
        degatsJoueur2 = degatsJoueur2.add(degatsShock.multiply(forMod2).substract(helmetOld.multiply(agiMod1)));
        // tour 3 boost force + double gun
        degatsJoueur2 = degatsJoueur2.add(degatsDoubleGun.multiply(getMod(forJoueur2.add(boost))).substract(helmetOld.multiply(agiMod1)));
        degatsJoueur2 = degatsJoueur2.add(degatsDoubleGunLeger.multiply(getMod(forJoueur2.add(boost))).substract(helmetOld.multiply(agiMod1)));
        // tour 4 double gun * 2
        degatsJoueur2 = degatsJoueur2.add(degatsDoubleGun.multiply(getMod(forJoueur2.add(boost))));
        degatsJoueur2 = degatsJoueur2.add(degatsDoubleGunLeger.multiply(getMod(forJoueur2.add(boost))));
        degatsJoueur2 = degatsJoueur2.add(degatsDoubleGun.multiply(getMod(forJoueur2.add(boost))));
        degatsJoueur2 = degatsJoueur2.add(degatsDoubleGunLeger.multiply(getMod(forJoueur2.add(boost))));

        System.out.println(soinsJoueur1);
        System.out.println(soinsJoueur2);
        System.out.println(degatsJoueur1);
        System.out.println(degatsJoueur2);

        System.out.println("Joueur 1: " + degatsJoueur1.substract(soinsJoueur2));
        System.out.println("Joueur 2: " + degatsJoueur2.substract(soinsJoueur1));




        soinsJoueur1 = new BigDecimalRange();
        degatsJoueur1 = new BigDecimalRange();
        // Joueur 1 vs Joueur 3
        // tour 1 casque + guerison
        soinsJoueur1 = soinsJoueur1.add(soinsCure.multiply(agiMod1));
        // tour 2 shotgun + bandage
        degatsJoueur1 = degatsJoueur1.add(degatsShootGun.multiply(forMod1).substract(helmetOld.multiply(agiMod3)));
        soinsJoueur1 = soinsJoueur1.add(soinsBandage.multiply(agiMod1));
        // tour 3 boost force + shotgun
        degatsJoueur1 = degatsJoueur1.add(degatsShootGun.multiply(getMod(forJoueur1.add(boost))).substract(helmetOld.multiply(agiMod3)));
        // tour 4 shootgun + etincelle
        degatsJoueur1 = degatsJoueur1.add(degatsShootGun.multiply(getMod(forJoueur1.add(boost))));
        degatsJoueur1 = degatsJoueur1.add(degatsSpark.multiply(getMod(forJoueur1.add(boost))));

        // tour 1 casque + flamme
        degatsJoueur3 = degatsJoueur3.add(degatsFlamme.multiply(forMod3).substract(helmetOld.multiply(agiMod1)));
        // tour 2 guerison + flamme
        soinsJoueur3 = soinsJoueur3.add(soinsCure.multiply(agiMod3));
        degatsJoueur3 = degatsJoueur3.add(degatsFlamme.multiply(forMod3).substract(helmetOld.multiply(agiMod1)));
        // tour 3 boost force + flamme
        degatsJoueur3 = degatsJoueur3.add(degatsFlamme.multiply(getMod(forJoueur3.add(boost))).substract(helmetOld.multiply(agiMod1)));
        // tour 4 double gun * 2
        degatsJoueur3 = degatsJoueur3.add(degatsDoubleGun.multiply(getMod(forJoueur2.add(boost))));
        degatsJoueur3 = degatsJoueur3.add(degatsDoubleGunLeger.multiply(getMod(forJoueur2.add(boost))));
        degatsJoueur3 = degatsJoueur3.add(degatsDoubleGun.multiply(getMod(forJoueur2.add(boost))));
        degatsJoueur3 = degatsJoueur3.add(degatsDoubleGunLeger.multiply(getMod(forJoueur2.add(boost))));

        System.out.println("Joueur 1: " + degatsJoueur1.substract(soinsJoueur3));
        System.out.println("Joueur 3: " + degatsJoueur3.substract(soinsJoueur1));
    }

    public static BigDecimal getMod(BigDecimal carac) {
        return new BigDecimal("1").add(carac.divide(new BigDecimal("100")));
    }

    public static class Leek {
        private BigDecimal agiBase = new BigDecimal("0");
        private BigDecimal forBase = new BigDecimal("0");
        List<Effect> agiBuffes = new ArrayList<>();
        List<Effect> forBuffes = new ArrayList<>();
        List<Effect> shields = new ArrayList<>();
        List<Action> attacks = new ArrayList<>();
        List<Action> heals = new ArrayList<>();

        public Leek(BigDecimal agiBase, BigDecimal forBase) {
            this.agiBase = agiBase;
            this.forBase = forBase;
        }

        public BigDecimal getAgi(int tour) {
            BigDecimal agi = agiBase;
            for (Effect effect: agiBuffes) {
                if (effect.contains(tour)) {
                    agi = agi.add(effect.getValue());
                }
            }

            return agi;
        }

        public BigDecimal getFor(int tour) {
            BigDecimal agi = forBase;
            for (Effect effect: forBuffes) {
                if (effect.contains(tour)) {
                    agi = agi.add(effect.getValue());
                }
            }

            return agi;
        }

        public void addAgiBuff(AbsoluteEffect agiBuff, int tour) {
            agiBuffes.add(new Effect(agiBuff, tour));
        }

        public void addForBuff(AbsoluteEffect forBuff, int tour) {
            forBuffes.add(new Effect(forBuff, tour));
        }

        public void addShield(AbsoluteEffect shield, int tour) {
            shields.add(new Effect(shield, tour));
        }

        public void addAttack(BigDecimalRange attack, int tour) {
            attacks.add(new Action(attack, tour));
        }

        public void addHeal(BigDecimalRange heal, int tour) {
            heals.add(new Action(heal, tour));
        }

        public BigDecimal getShield(int tour) {
            BigDecimal armor = BigDecimal.ZERO;
            for (Effect shield: shields) {
                if (shield.contains(tour)) {
                    BigDecimal agiMod = getMod(getAgi(shield.getStart()));
                    armor = armor.add(shield.getValue().multiply(agiMod));
                }
            }

            return armor;
        }

        public BigDecimalRange getTotalHeals() {
            BigDecimalRange totalHeals = new BigDecimalRange();
            for (Action heal: heals) {
                BigDecimal agiMod = getMod(getAgi(heal.getTour()));
                BigDecimalRange healsTurn = heal.getRange().multiply(agiMod);
                totalHeals = totalHeals.add(healsTurn);
            }

            return totalHeals;
        }

        public BigDecimalRange getTotalDamage(Leek joueur) {
            BigDecimalRange totalDamages = new BigDecimalRange();
            for (Action attack: attacks) {
                BigDecimal forMod = getMod(getFor(attack.getTour()));
                BigDecimal enemyShield = joueur.getShield(attack.getTour());
                BigDecimalRange rawDamages = attack.getRange().multiply(forMod);
                BigDecimalRange damagesTurn = rawDamages.substract(enemyShield);
                totalDamages = totalDamages.add(damagesTurn);
//                System.out.println(attack.getTour() + " : " + attack.getRange() + " -> " + rawDamages + " -> " + damagesTurn + " -> " + totalDamages);
            }

            return totalDamages;
        }

        public BigDecimalRange versus(Leek joueur) {
            return getTotalDamage(joueur).substract(joueur.getTotalHeals());
        }

        private class Effect {
            private int start;
            private int end;
            private BigDecimal value;

            public Effect(AbsoluteEffect eff, int tour) {
                this.start = tour;
                this.end = tour + eff.getDuration() - 1;
                this.value = eff.getValue();
            }

            public boolean contains(int test) {
                return start <= test && test <= end;
            }

            /** @return the end. */
            public int getEnd() {
                return end;
            }

            /** @return the value. */
            public BigDecimal getValue() {
                return value;
            }

            /** @return the start. */
            public int getStart() {
                return start;
            }
        }

        private class Action {
            private BigDecimalRange range;
            private int tour;

            public Action(BigDecimalRange range, int tour) {
                this.range = range;
                this.tour = tour;
            }

            /** @return the tour. */
            public int getTour() {
                return tour;
            }

            /** @return the range. */
            public BigDecimalRange getRange() {
                return range;
            }
        }
    }

    public static class AbsoluteEffect {
        private BigDecimal value;
        private int duration;

        public AbsoluteEffect(BigDecimal value, int duration) {
            this.value = value;
            this.duration = duration;
        }

        /** @return the duration. */
        public int getDuration() {
            return duration;
        }

        /** @return the value. */
        public BigDecimal getValue() {
            return value;
        }
    }

    public static class BigDecimalRange extends Range {
        private BigDecimal minimum;
        private BigDecimal maximum;

        public BigDecimalRange() {
            this(BigDecimal.ZERO, BigDecimal.ZERO);
        }

        public BigDecimalRange(BigDecimal minimum, BigDecimal maximum) {
            this.minimum = minimum;
            this.maximum = maximum;
        }


        /**
         * <p>Gets the minimum number in this range.</p>
         *
         * @return the minimum number in this range
         */
        @Override
        public BigDecimal getMinimumNumber() {
            return minimum;
        }

        /**
         * <p>Gets the maximum number in this range.</p>
         *
         * @return the maximum number in this range
         */
        @Override
        public BigDecimal getMaximumNumber() {
            return maximum;
        }

        /**
         * <p>Tests whether the specified <code>Number</code> occurs within
         * this range.</p>
         * <p/>
         * <p>The exact comparison implementation varies by subclass. It is
         * intended that an <code>int</code> specific subclass will compare using
         * <code>int</code> comparison.</p>
         * <p/>
         * <p><code>null</code> is handled and returns <code>false</code>.</p>
         *
         * @param number the number to test, may be <code>null</code>
         * @return <code>true</code> if the specified number occurs within this range
         * @throws IllegalArgumentException if the <code>Number</code> cannot be compared
         */
        @Override
        public boolean containsNumber(Number number) {
            return minimum.doubleValue() <= number.doubleValue()
                    && number.doubleValue() <= maximum.doubleValue();
        }

        public BigDecimalRange multiply(BigDecimal multiply) {
            return new BigDecimalRange(minimum.multiply(multiply), maximum.multiply(multiply));
        }

        public BigDecimalRange add(BigDecimalRange range) {
            return new BigDecimalRange(minimum.add(range.getMinimumNumber()), maximum.add(range.getMaximumNumber()));
        }

        public BigDecimalRange substract(BigDecimal substract) {
            BigDecimal min = minimum.subtract(substract);
            BigDecimal max = maximum.subtract(substract);
            if (min.compareTo(BigDecimal.ZERO) < 0) {
                min = BigDecimal.ZERO;
            }
            if (max.compareTo(BigDecimal.ZERO) < 0) {
                max = BigDecimal.ZERO;
            }
            return new BigDecimalRange(min, max);
        }

        public BigDecimalRange substract(BigDecimalRange range) {
            return new BigDecimalRange(minimum.subtract(range.maximum), maximum.subtract(range.minimum));
        }

        @Override
        public String toString() {
            return "[" + minimum + ", " + maximum + "]";
        }
    }
}
